﻿using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.CodeMarkers.Internal;

namespace Microsoft.CodeMarkers
{
    public class CodeMarkerWaiter<TEnum> : IDisposable
    {
        ManualResetEvent X { get; set; }
        CodeMarkerClient Client { get; set; }
        List<int> Markers { get; set; }
        TimeSpan Timeout { get; set; }
        List<TEnum> EnumMarkers { get; set; }

        public CodeMarkerWaiter(params TEnum[] markers)
            : this(TimeSpan.FromSeconds(10), markers)
        {
        }
        
        public CodeMarkerWaiter(TimeSpan timeout,params TEnum[] markers)
        {
            if (CodeMarker.ClientHost == null)
            {
                throw new InvalidOperationException("Please call CodeMarker.InitializeClient prior to using this method");
            }

            this.X = new ManualResetEvent(false);
            this.Markers = new List<int>();
            this.EnumMarkers = new List<TEnum>();
            this.Timeout = timeout;

            if (markers.Length > 0)
            {
                foreach (TEnum m in markers)
                {
                    this.Markers.Add(Convert.ToInt32(m));
                    this.EnumMarkers.Add(m);
                }
            }
            else
            {
                throw new ArgumentException("At least one marker must be specified", "markers");
            }

            this.Client = CodeMarker.ClientHost.Client;
            this.Client.MarkerRaised += this.OnMarkerRecieved;
        }

        private void OnMarkerRecieved(int markerValue)
        {
            if (this.Markers.Contains(markerValue))
            {
                X.Set();
            }
        }

        public void WaitFor()
        {
            WaitFor(this.Timeout);
        }

        public void WaitFor(TimeSpan timeout)
        {
            if (!X.WaitOne(timeout))
            {
                throw new TimeoutException(String.Format("Timed out waiting for marker. Timeout '{0}'. Markers '{1}'", this.Timeout, string.Join(",", this.EnumMarkers)));
            }
        }

        public void Dispose()
        {
            WaitFor();
            this.Client.MarkerRaised -= this.OnMarkerRecieved;
        }
    }
}
